package yxy.banana.socket.controller.server;

import yxy.apple.protobuf.Constants;
import yxy.apple.protobuf.Message.Parcal;
import yxy.apple.protobuf.external.gateway.Gateway.AuthenticationRequest;
import yxy.apple.protobuf.external.gateway.Gateway.AuthenticationResponse;
import yxy.apple.util.scheduler.Schedulable;
import yxy.apple.util.scheduler.ScheduledFuture;
import yxy.banana.socket.NetworkElement;
import yxy.banana.socket.controlled.ControlledHandler;

public abstract class ServerAuthenticationController<H extends ControlledHandler> extends ServerHeartbeatController<H> {

	private int timeout = 10000;

	private ScheduledFuture watching = null;

	private boolean authenticated = false;

	private class AuthenticationListener implements Schedulable {

		@Override
		public void trigger() {
			ServerAuthenticationController.this.watching = null;
			ServerAuthenticationController.this.unauthenticated();
		}

		@Override
		public void cancelled() {
		}
	}

	public ServerAuthenticationController(int heartbeatTimeout, int authenticationTimeout) {
		super(heartbeatTimeout);
		this.timeout = authenticationTimeout;
	}

	public boolean authenticated() {
		return this.authenticated;
	}

	@Override
	public void open() {
		super.open();
		this.watching = ServerRegistryController.scheduler.schedule(new AuthenticationListener(), this.timeout);
	}

	@Override
	public void close() {
		if (this.watching != null) {
			this.watching.cancel();
			this.watching = null;
		}
		super.close();
	}

	@Override
	public boolean control(Parcal parcal) throws Exception {
		if (super.control(parcal)) {
			return true;
		} else if (parcal.getProto() == Constants.ControlProto.AuthenticationRequest) {
			boolean result = false;
			try {
				AuthenticationRequest request = AuthenticationRequest.parseFrom(parcal.getData());
				String ethic = request.getEthic();
				int uid = request.getUid();
				String token = request.getToken();
				// 不允许重复认证
				if (!this.authenticated && this.authenticate(ethic, uid, token)) {
					this.authenticated = true;
					result = true;
					if (this.watching != null) {
						this.watching.cancel();
						this.watching = null;
					}
					this.authenticated(ethic, uid, token);
				}
				try {
					AuthenticationResponse.Builder builder = AuthenticationResponse.newBuilder();
					builder.setResult(result);
					AuthenticationResponse response = builder.build();
					long s = NetworkElement.value(this.handler().system(), this.handler().cluster(), this.handler().channel());
					long d = parcal.getSource();
					this.handler().send(parcal.getTag(), s, d, Constants.ControlProto.AuthenticationResponse, response);
				} catch (Exception e) {
					this.caught(e);
				}
			} catch (Exception e) {
				this.caught(e);
			}
			if (!result) {
				this.unauthenticated();
			}
			return true;
		} else {
			return false;
		}
	}

	/**
	 * 处理认证
	 * 
	 * @param uid
	 * @param token
	 * @return
	 */
	protected abstract boolean authenticate(String ethic, int uid, String token);

	/**
	 * 认证成功时回调
	 */
	protected abstract void authenticated(String ethic, int uid, String token);

	/**
	 * 未完成认证时回调
	 */
	protected abstract void unauthenticated();
}
